﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Cofe.Core.Utils;
using Cofe.Core.Validater;

namespace Cofe.Core.Property
{
    public class PropertyAttributeHelper : IPropertyAttributeHelper
    {
        #region Constructor

        public PropertyAttributeHelper()
        {

        }

        #endregion

        #region Methods

        private void cacheProperty(object property)
        {
            List<Attribute> attributes = new List<Attribute>();

            attributes.AddRange(AttributeUtils.GetCustomAttributes(property));

            _propertyCache[property] = attributes;
        }

        public T FindAttribute<T>(object property) where T : Attribute
        {
            if (!(_propertyCache.ContainsKey(property)))
                cacheProperty(property);

            return _propertyCache[property].Where(a => a is T)
                .Select(a => a as T).FirstOrDefault();
        }


        public PropertyCacheOptions GetPropertyCacheOption(object property)
        {
            var pcoa = FindAttribute<CachableAttribute>(property);
            if (pcoa != null)
                return pcoa.CacheOptions;
            return PropertyCacheOptions.Default;
        }

        public DatabaseCacheOptions GetDatabaseCacheOption(object property)
        {
            var pcoa = FindAttribute<CachableAttribute>(property);
            if (pcoa != null)
                return pcoa.DatabaseCacheOptions;
            return DatabaseCacheOptions.NotCachable;
        }

        public Type GetDefaultType(object property)
        {
            if (!(_propertyType.ContainsKey(property)))
            {
                var pta = FindAttribute<PropertyTypeAttribute>(property);
                if (pta == null)
                    throw new Exception(String.Format("{0} does not have PropertyTypeAttribute defined.", property));
                _propertyType[property] = pta.Types[0];
            }
            return _propertyType[property];
        }

        public bool HasAlias(object property)
        {
            return getAlias(property) != null;
        }

        /// <summary>
        /// Return alias of the specified property, or property.ToString() if it's not specified.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="item"></param>
        /// <returns></returns>
        public string getAlias(object property)
        {
            if (!_propertyAlias.ContainsKey(property))
            {
                var aa = FindAttribute<AliasAttribute>(property);

                //If Alias != null, Alias
                //If not specified or aa.FullQualifiedName = true, FullQualified Name.
                //Otherwise (aa.FullQualifiedName = false) return property name.

                string retVal = null;
                if (aa == null)
                    retVal = EnumUtils.GetFullName(property);
                else if (aa.Alias != null)
                    retVal = aa.Alias;
                else if (aa.FullQualifiedName)
                    retVal = EnumUtils.GetFullName(property);
                else retVal = property.ToString();

                _propertyAlias[property] = retVal;
            }

            return _propertyAlias[property];
        }

        public bool HasDefaultValue(object property)
        {
            var pta = FindAttribute<PropertyTypeAttribute>(property);
            return pta != null && pta.DefaultValue != null;
        }

        public object GetDefaultValue(object property)
        {
            var pta = FindAttribute<PropertyTypeAttribute>(property);
            return pta == null ? null : pta.DefaultValue;
        }

      

        #endregion


        #region Data

        ConcurrentDictionary<object, List<Attribute>> _propertyCache = new ConcurrentDictionary<object, List<Attribute>>();
        ConcurrentDictionary<object, string> _propertyAlias = new ConcurrentDictionary<object, string>();
        ConcurrentDictionary<object, Type> _propertyType = new ConcurrentDictionary<object, Type>();

        IPropertyDictionary _propertyDictionary = null;

        #endregion

        #region Public Properties

        public IPropertyDictionary PropertyDictionary
        {
            get { return _propertyDictionary ?? (_propertyDictionary = CofeServices.ServiceLocater.FindService<IPropertyDictionary>()); }
            set { _propertyDictionary = value; }
        }

        #endregion


    }
}
